/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_spm.c
 *
 * @brief This file provides system power manage module integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_spm.h"

/* ============================================  Define  ============================================ */
#define REG_DEBUG_MODE_ADDR                 0x40008050UL
#define DEBUG_MODE_EN_MASK                  0x78030001UL
#define REG_DEBUG_MGR_CFG1_ADDR             0x40008058UL

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* for SPM Event call back function */
static DeviceCallback_Type s_spmCallback = NULL;
/* for LVD Event call back function */
static DeviceCallback_Type s_lvdCallback = NULL;

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
 * @brief Set the low power mode.
 *
 * @param[in] mode: low power mode, value can be
 *                 - LOW_POWER_MODE_STOP
 *                 - LOW_POWER_MODE_STANDBY
 * @return none
 */
void SPM_SetLowPowerMode(SPM_PowerModeType mode)
{
    DEVICE_ASSERT(IS_POWER_MODE_PARA(mode));

    MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_SLEEP_MODE_Msk, SPM_PWR_MGR_CFG0_SLEEP_MODE_Pos, (uint32_t)mode);
    SET_BIT32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_PWR_EN_Msk);

    NVIC_EnableIRQ(SPM_IRQn);
}

/*!
 * @brief Enable fast wakeup mode.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableFastBoot(ACTION_Type enable)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_FAST_BOOT_Msk, SPM_PWR_MGR_CFG0_EN_FAST_BOOT_Pos, (uint32_t)enable);
}

/*!
 * @brief Enable low voltage detect.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableLVD(ACTION_Type enable)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_LVD_Msk, SPM_PWR_MGR_CFG0_EN_LVD_Pos, (uint32_t)enable);
    if (enable)
    {
        WRITE_MEM32(REG_DEBUG_MODE_ADDR, DEBUG_MODE_EN_MASK);
        MODIFY_MEM32(REG_DEBUG_MGR_CFG1_ADDR, 0x40U, 6U, 0x0U);  /* For ES1 */
    }
}

/*!
 * @brief Set LVD voltage threshold.
 *
 * @param[in] threahold: 1: high threshold, 0: low threshold
 * @return none
 */
void SPM_SetLVDThreshold(uint8_t threahold)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_LVD_THL_Msk, SPM_PWR_MGR_CFG1_LVD_THL_Pos, threahold);
}

/*!
 * @brief Set LVD Handler callback function.
 *
 * @param[in] eventFunc: the pointer of the call back function, which will be called in LVD_IRQHandler
 * @return none
 */
void LVD_SetCallback(const DeviceCallback_Type eventFunc)
{
    s_lvdCallback = eventFunc;
}

/*!
 * @brief Enable low voltage reset.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableLVR(ACTION_Type enable)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_LVR_Msk, SPM_PWR_MGR_CFG0_EN_LVR_Pos, (uint32_t)enable);
    if (enable)
    {
        WRITE_MEM32(REG_DEBUG_MODE_ADDR, DEBUG_MODE_EN_MASK);
        MODIFY_MEM32(REG_DEBUG_MGR_CFG1_ADDR, 0x60U, 5U, 0x0U);  /* For ES1 */
    }
}

/*!
 * @brief Enable internal LDO low voltage reset.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableDPWRLVR(ACTION_Type enable)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_DPWRLVR_Msk, SPM_PWR_MGR_CFG0_EN_DPWRLVR_Pos, (uint32_t)enable);
}

/*!
 * @brief Set LVR voltage threshold.
 *
 * @param[in] threahold: 1: high threshold, 0: low threshold
 * @return none
 */
void SPM_SetLVRThreshold(uint8_t threahold)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_LVR_THL_Msk, SPM_PWR_MGR_CFG1_LVR_THL_Pos, threahold);
}

/*!
 * @brief Enable XOSC bypass mode.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableXOSCBypassMode(ACTION_Type enable)
{
    MODIFY_REG32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_XOSC_HSEBYP_Msk, SPM_PWR_MGR_CFG1_XOSC_HSEBYP_Pos, (uint32_t)enable);
}

/*!
 * @brief Enable XOSC and wait the XOSC status is ready.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return SUCCESS: xosc set success
 *           ERROR: xosc set fail
 */
ERROR_Type SPM_EnableXOSC(ACTION_Type enable)
{
    uint32_t status = 0U;
    uint32_t timeout = SPM_TIMEOUT_VALUE;
    ERROR_Type ret = SUCCESS;

    if (ENABLE == enable)
    {
        /* Enable XOSC */
        SET_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_XOSC_HSEON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_XOSC_RDY_Msk);
            timeout--;
        } while ((!status) && (timeout != 0U));
    }
    else
    {
        /* Disable XOSC */
        CLEAR_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_XOSC_HSEON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_XOSC_RDY_Msk);
            timeout--;
        } while ((status) && (timeout != 0U));
    }

    /* Check whether waiting for XOSC timeout */
    if (0U == timeout)
    {
        ret = ERROR;
    }

    return ret;
}

/*!
 * @brief Enable PLL and wait for the status is ready.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return SUCCESS: pll set success
 *           ERROR: pll set fail
 */
ERROR_Type SPM_EnablePLL(ACTION_Type enable)
{
    uint32_t status = 0U;
    uint32_t timeout = SPM_TIMEOUT_VALUE;
    ERROR_Type ret = SUCCESS;

    if (ENABLE == enable)
    {
        /* Enable SPLL */
        SET_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SYSPLL_ON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SYSPLL_RDY_Msk);
            timeout--;
        } while ((!status) && (timeout != 0U));
    }
    else
    {
        /* Disable SPLL */
        CLEAR_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SYSPLL_ON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SYSPLL_RDY_Msk);
            timeout--;
        } while ((status) && (timeout != 0U));
    }

    /* Check whether waiting for SPLL timeout */
    if (0U == timeout)
    {
        ret = ERROR;
    }

    return ret;
}

/*!
 * @brief Enable SXOSC and wait the SXOSC status is ready.
 *
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return SUCCESS: sxosc set success
 *           ERROR: sxosc set fail
 */
ERROR_Type SPM_EnableSXOSC(ACTION_Type enable)
{
    uint32_t status = 0U;
    uint32_t timeout = SPM_TIMEOUT_VALUE * 100U;
    ERROR_Type ret = SUCCESS;

    if (ENABLE == enable)
    {
        /* Enable SXOSC */
        SET_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SXOSC_ON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SXOSC_RDY_Msk);
            timeout--;
        } while ((!status) && (timeout != 0U));
    }
    else
    {
        /* Disable SXOSC */
        CLEAR_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SXOSC_ON_Msk);
        do
        {
            status = READ_BIT32(SPM->PWR_MGR_CFG1, SPM_PWR_MGR_CFG1_SXOSC_RDY_Msk);
            timeout--;
        } while ((status) && (timeout != 0U));
    }

    /* Check whether waiting for SXOSC timeout */
    if (0U == timeout)
    {
        ret = ERROR;
    }

    return ret;
}

/*!
 * @brief Enable CAN lowpass filter.
 *
 * @param[in] canIndex: CAN0/1
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableCANLowpassFilter(uint8_t canIndex, ACTION_Type enable)
{
    if (0U == canIndex)
    {
        MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_CAN0_FILTER_Msk, SPM_PWR_MGR_CFG0_EN_CAN0_FILTER_Pos, (uint32_t)enable);
    }
    else if (1U == canIndex)
    {
        MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_CAN1_FILTER_Msk, SPM_PWR_MGR_CFG0_EN_CAN1_FILTER_Pos, (uint32_t)enable);
    }
}

/*!
 * @brief Config module wakeup function.
 *
 * @param[in] module: module enable in SPM, value can be
 *                   - SPM_MODULE_ACMP0
 *                   - SPM_MODULE_I2C0
 *                   - SPM_MODULE_I2C1
 *                   - SPM_MODULE_SPI0
 *                   - SPM_MODULE_SPI1
 *                   - SPM_MODULE_CAN0
 *                   - SPM_MODULE_CAN1
 *                   - SPM_MODULE_UART0
 *                   - SPM_MODULE_UART1
 *                   - SPM_MODULE_UART2
 *                   - SPM_MODULE_RTC
 *                   - SPM_MODULE_ADC0
 *                   - SPM_MODULE_GPIO
 *                   - SPM_MODULE_NMI
 *                   - SPM_MODULE_LVD
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableModuleWakeup(SPM_ModuleType module, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_SPM_MODULE_PARA(module));

    if (ENABLE == enable)
    {
        SPM->EN_PERIPH_WAKEUP |= ((uint32_t)1U << ((uint32_t)module));
    }
    else
    {
        SPM->EN_PERIPH_WAKEUP &= ~((uint32_t)1U << ((uint32_t)module));
    }
}

/*!
 * @brief Enable which module the SPM wait the ack when do stop.
 *
 * @param[in] module: module enable in SPM, value can be
 *                   - SLEEP_ACK_ACMP0
 *                   - SLEEP_ACK_I2C0
 *                   - SLEEP_ACK_I2C1
 *                   - SLEEP_ACK_SPI0
 *                   - SLEEP_ACK_SPI1
 *                   - SLEEP_ACK_CAN0
 *                   - SLEEP_ACK_CAN1
 *                   - SLEEP_ACK_UART0
 *                   - SLEEP_ACK_UART1
 *                   - SLEEP_ACK_UART2
 *                   - SLEEP_ACK_DMA0
 *                   - SLEEP_ACK_ADC0
 *                   - SLEEP_ACK_EFLASH
 * @param[in] enable: enable state
 *                   - ENABLE
 *                   - DISABLE
 * @return none
 */
void SPM_EnableModuleSleepACK(SPM_SleepACKType module, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_SLEEP_ACK_PARA(module));

    if (ENABLE == enable)
    {
        SPM->EN_PERIPH_SLEEP_ACK |= ((uint32_t)1U << ((uint32_t)module));
    }
    else
    {
        SPM->EN_PERIPH_SLEEP_ACK &= ~((uint32_t)1U << ((uint32_t)module));
    }
}

/*!
 * @brief Get sleep ack status.
 *
 * @param[in] none
 * @return sleep ack status value
 */
uint32_t SPM_GetModuleSleepACKStatus(void)
{
    return (SPM->PERIPH_SLEEP_ACK_STATUS);
}

/*!
 * @brief Get the wakeup source flag.
 *
 * @param[in] none
 * @return the wakeup source flag
 */
uint32_t SPM_GetModuleWakeupSourceFlag(void)
{
    return (SPM->WAKEUP_IRQ_STATUS);
}

/*!
 * @brief Clear the wakeup source flag.
 *
 * @param[in] none
 * @return none
 */
void SPM_ClearWakeupSourceFlag(void)
{
    uint32_t flag = SPM->WAKEUP_IRQ_STATUS;

    SPM->WAKEUP_IRQ_STATUS = flag;
}

/*!
 * @brief Set SPM IRQHandler callback function.
 *
 * @param[in] eventFunc: the pointer of the call back function, which will be called in SPM_IRQHandler
 * @return none
 */
void SPM_SetCallback(const DeviceCallback_Type eventFunc)
{
    s_spmCallback = eventFunc;
}

/*!
 * @brief SPM IRQHandler.
 *
 * @param[in] none
 * @return none
 */
void SPM_IRQHandler(void)
{
    uint32_t wakeupFlag = SPM_GetModuleWakeupSourceFlag();

    SPM->WAKEUP_IRQ_STATUS = wakeupFlag;

    if (s_spmCallback)
    {
        s_spmCallback((void *)SPM, wakeupFlag, SPM->PERIPH_SLEEP_ACK_STATUS);
    }
}

/*!
 * @brief LVD IRQHandler.
 *
 * @param[in] none
 * @return none
 */
void LVD_IRQHandler(void)
{
    uint32_t wakeupFlag = SPM_GetModuleWakeupSourceFlag();

    SPM->WAKEUP_IRQ_STATUS = wakeupFlag;

    if (s_lvdCallback)
    {
        s_lvdCallback((void *)SPM, wakeupFlag, 0U);
    }
}

/* =============================================  EOF  ============================================== */
